<?php

class Wall_Model_DbTable_Actions extends Engine_Db_Table
{

    protected $_name = 'activity_actions';
    protected $_rowClass = 'Wall_Model_Action';
    protected $_serializedColumns = array('params');
    protected $_actionTypes;

    public function addActivity(Core_Model_Item_Abstract $subject, Core_Model_Item_Abstract $object, $type, $body = null, array $params = null)
    {
        // Disabled or missing type
        $typeInfo = $this->getActionType($type);
        if (!$typeInfo || !$typeInfo->enabled)
        {
            return;
        }

        // User disabled publishing of this type
        $actionSettingsTable = Engine_Api::_()->getDbtable('actionSettings', 'activity');
        if (!$actionSettingsTable->checkEnabledAction($subject, $type))
        {
            return;
        }

        // Create action
        $action = $this->createRow();
        $action->setFromArray(array(
            'type' => $type,
            'subject_type' => $subject->getType(),
            'subject_id' => $subject->getIdentity(),
            'object_type' => $object->getType(),
            'object_id' => $object->getIdentity(),
            'body' => (string) $body,
            'params' => (array) $params,
            'date' => date('Y-m-d H:i:s')
        ));
        $action->save();

        // Add bindings
        $this->addActivityBindings($action, $type, $subject, $object);

        // We want to update the subject
        if (isset($subject->modified_date))
        {
            $subject->modified_date = date('Y-m-d H:i:s');
            $subject->save();
        }

        return $action;
    }

    public function getActivity(User_Model_User $user, array $params = array(), &$id = NULL, &$end_of_feed = false)
    {
        extract($this->_getInfo($params)); // action_id, limit, min_id, max_id

        $streamTable = Engine_Api::_()->getDbtable('stream', 'activity');

        $db = $streamTable->getAdapter();
        $union = new Zend_Db_Select($db);

        $masterActionTypes = Engine_Api::_()->getDbtable('actionTypes', 'activity')->getActionTypes();
        $mainActionTypes = array();

        foreach ($masterActionTypes as $type)
        {
            if ($type->displayable & 4)
            {
                $mainActionTypes[] = $type->type;
            }
        }

        if (empty($mainActionTypes))
        {
            return null;
        }
        else if (count($mainActionTypes) == count($masterActionTypes))
        {
            $mainActionTypes = true;
        }
        else
        {
            $mainActionTypes = "'" . join("', '", $mainActionTypes) . "'";
        }
        
        // Prepare sub queries
        $event = Engine_Hooks_Dispatcher::getInstance()->callEvent('getActivity', $user);
        $responses = (array) $event->getResponses();

        if (empty($responses))
        {
            return null;
        }
        
        foreach ($responses as $response)
        {
            if (empty($response))
                continue;

            $select = $streamTable->select()
                    ->from($streamTable->info('name'), 'action_id')
                    ->where('target_type = ?', $response['type'])
            ;

            if (empty($response['data']))
            {
                // Simple
                $select->where('target_id = ?', 0);
            }
            else if (is_scalar($response['data']) || count($response['data']) === 1)
            {
                // Single
                if (is_array($response['data']))
                {
                    list($response['data']) = $response['data'];
                }
                $select->where('target_id = ?', $response['data']);
            }
            else if (is_array($response['data']))
            {
                // Array
                $select->where('target_id IN(?)', (array) $response['data']);
            }
            else
            {
                // Unknown
                continue;
            }

            // Add action_id/max_id/min_id
            if (null !== $action_id)
            {
                $select->where('action_id = ?', $action_id);
            }
            else
            {
                if (null !== $min_id)
                {
                    $select->where('action_id >= ?', $min_id);
                }
                else if (null !== $max_id)
                {
                    $select->where('action_id <= ?', $max_id);
                }
            }

            if ($mainActionTypes !== true)
            {
                $select->where('type IN(' . $mainActionTypes . ')');
            }

            // Add order/limit
            $select->order('action_id DESC')
                    ->limit($limit);

            // Add to main query

            $union->union(array('(' . $select->__toString() . ')')); // (string) not work before PHP 5.2.0
        }

        // Finish main query
        $union->order('action_id DESC')->limit($limit);

        //Zend_Registry::get('Zend_Log')->log($union, Zend_Log::DEBUG);
        // Get actions
        $actions = $db->fetchAll($union);

        // No visible actions
        if (empty($actions))
        {
            //Fix: after check update, end of feed = true
//            if (!$ajax_call){
//              $end_of_feed = true;  
//            }
            $id = null;
            return null;
        }
        
        // Process ids
        $ids = array();
        foreach ($actions as $data)
        {
            $ids[] = $data['action_id'];
        }
        $ids = array_unique($ids);

        //Caculate next_id and end_of_feed
        $select_no_filter = $this->select()
                ->where('action_id IN(' . join(',', $ids) . ')')
                ->order('action_id DESC')
                ->limit($limit);
        $actions_array = $this->fetchAll($select_no_filter)->toArray();
        if (count($actions_array) > 0)
        {
            $last_action = end($actions_array);
            $id = $last_action['action_id'] - 1;
        }
        if (count($actions_array) < $limit || count($actions_array) <= 0)
        {
            $end_of_feed = true;
        }

        // Privacy check
        $uid = (isset($user->user_id) ? $user->user_id : 0);
        $wall_tb = 'engine4_wall_privacies';

        $select = $this->select()
                ->setIntegrityCheck(false)
                ->from(array('a' => 'engine4_activity_actions'))
                ->joinLeft(array('p' => "$wall_tb"), 'p.action_id = a.action_id', 'p.privacy')
                ->where('a.action_id IN(' . join(',', $ids) . ')')
                ->where("(a.action_id NOT IN (SELECT action_id FROM $wall_tb)
              OR p.user_id = $uid
              OR (p.privacy = 2 AND p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid AND resource_approved = 1 AND user_approved = 1))
              OR (p.privacy = 1 AND (p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid)
                                     OR p.user_id IN (SELECT DISTINCTROW m2.resource_id
                                                  FROM engine4_user_membership AS m1, engine4_user_membership AS m2
                                                  WHERE m1.user_id = $uid
                                                        AND m1.resource_id = m2.user_id
                                                            AND (m1.resource_approved = 1 AND m1.user_approved = 1)
                                                                AND (m2.resource_approved = 1 AND m2.user_approved = 1))
                                     )
                 )
                  OR (p.privacy > 2 AND $uid in (SELECT object_id FROM `engine4_wall_lists` as `list`
                                            LEFT JOIN `engine4_wall_listitems` as `listitem`
                                            ON list.list_id = listitem.list_id 
                                            WHERE list.list_id = p.privacy 
                                                AND object_type = 'friend' ))
                
              )");


        //$filter_by_list_id = null;
        switch ($filter_type)
        {
            case 'list':
                if (null != $filter_id)
                {
                    $select->where(" a.subject_type = 'user' and a.subject_id IN (
                			SELECT object_id FROM `engine4_wall_lists` as `list`
                			LEFT JOIN `engine4_wall_listitems` as `listitem`
                			ON list.list_id = listitem.list_id
                			WHERE list.list_id = $filter_id AND object_type = 'friend' )");
                }
                break;
            case 'friend':
                $select->where("a.subject_type = 'user' and a.subject_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid AND resource_approved = 1 AND user_approved = 1)");
                break;
            case 'blog':
                $select->where("a.object_type = 'blog'");
                break;
            case 'album':
                $select->where("a.object_type = 'album' or a.object_type = 'advalbum_album'");
                break;
            case 'status':
                $select->where("a.type = 'status'");
                break;
        }
        
//        if ($ajax_call){
//            $limit = 1;
//        }
        
        $select->order('a.action_id DESC')->limit($limit);
        //echo $select;die;
        // Finally get activity

        return $this->fetchAll($select);
    }

    public function getActivityAbout(Core_Model_Item_Abstract $about, User_Model_User $user, array $params = array(), &$id = NULL, &$end_of_feed = false)
    {
        // Proc args
        extract($this->_getInfo($params)); // action_id, limit, min_id, max_id
        // Prepare main query
        $streamTable = Engine_Api::_()->getDbtable('stream', 'activity');
        $db = $streamTable->getAdapter();
        $union = new Zend_Db_Select($db);

        // Prepare action types
        $masterActionTypes = Engine_Api::_()->getDbtable('actionTypes', 'activity')->getActionTypes();
        $subjectActionTypes = array();
        $objectActionTypes = array();

        foreach ($masterActionTypes as $type)
        {
            if ($type->displayable & 1)
            {
                $subjectActionTypes[] = $type->type;
            }
            if ($type->displayable & 2)
            {
                $objectActionTypes[] = $type->type;
            }
        }

        if (empty($subjectActionTypes) && empty($objectActionTypes))
        {
            return null;
        }

        if (empty($subjectActionTypes))
        {
            $subjectActionTypes = null;
        }
        else if (count($subjectActionTypes) == count($masterActionTypes))
        {
            $subjectActionTypes = true;
        }
        else
        {
            $subjectActionTypes = "'" . join("', '", $subjectActionTypes) . "'";
        }

        if (empty($objectActionTypes))
        {
            $objectActionTypes = null;
        }
        else if (count($objectActionTypes) == count($masterActionTypes))
        {
            $objectActionTypes = true;
        }
        else
        {
            $objectActionTypes[] = 'status';
            $objectActionTypes = "'" . join("', '", $objectActionTypes) . "'";
        }

        // Prepare sub queries
        $event = Engine_Hooks_Dispatcher::getInstance()->callEvent('getActivity', $user);
        $responses = (array) $event->getResponses();
        
        if (empty($responses))
        {
            return null;
        }

        foreach ($responses as $response)
        {
            if (empty($response))
                continue;

            // Target info
            $select = $streamTable->select()
                    ->from($streamTable->info('name'), 'action_id')
                    ->where('target_type = ?', $response['type'])
            ;
        
            if (empty($response['data']))
            {
                // Simple
                $select->where('target_id = ?', 0);
            }
            else if (is_scalar($response['data']) || count($response['data']) === 1)
            {
                // Single
                if (is_array($response['data']))
                {
                    list($response['data']) = $response['data'];
                }
                $select->where('target_id = ?', $response['data']);
            }
            else if (is_array($response['data']))
            {
                // Array
                $select->where('target_id IN(?)', (array) $response['data']);
            }
            else
            {
                // Unknown
                continue;
            }
            
            // Add action_id/max_id/min_id
            if (null !== $action_id)
            {
                $select->where('action_id = ?', $action_id);
            }
            else
            {
                if (null !== $min_id)
                {
                    $select->where('action_id >= ?', $min_id);
                }
                else if (null !== $max_id)
                {
                    $select->where('action_id <= ?', $max_id);
                }
            }

            // Add order/limit
            $select
                    ->order('action_id DESC')
                    ->limit($limit);


            // Add subject to main query
            $selectSubject = clone $select;
            if ($subjectActionTypes !== null)
            {
                if ($subjectActionTypes !== true)
                {
                    $selectSubject->where('type IN(' . $subjectActionTypes . ')');
                }
                $selectSubject
                        ->where('subject_type = ?', $about->getType())
                        ->where('subject_id = ?', $about->getIdentity());
                $union->union(array('(' . $selectSubject->__toString() . ')')); // (string) not work before PHP 5.2.0
            }

            // Add object to main query
            $selectObject = clone $select;
            if ($objectActionTypes !== null)
            {
                if ($objectActionTypes !== true)
                {
                    $selectObject->where('type IN(' . $objectActionTypes . ')');
                }
                $selectObject
                        ->where('object_type = ?', $about->getType())
                        ->where('object_id = ?', $about->getIdentity());
                $union->union(array('(' . $selectObject->__toString() . ')')); // (string) not work before PHP 5.2.0
            }
        }

        // Finish main query
        $union
                ->order('action_id DESC')
                ->limit($limit);

        // Get actions
        $actions = $db->fetchAll($union);
        
        // No visible actions
        if (empty($actions))
        {
            //Fix: after check update, end of feed = true
//            if (!$ajax_call){
//              $end_of_feed = true;  
//            }
            $id = null;
            return null;
        }

        // Process ids
        $ids = array();
        foreach ($actions as $data)
        {
            $ids[] = $data['action_id'];
        }
        $ids = array_unique($ids);


        //Caculate next_id and end_of_feed
        $select_no_filter = $this->select()
                ->where('action_id IN(' . join(',', $ids) . ')')
                ->order('action_id DESC')
                ->limit($limit);

        $actions_array = $this->fetchAll($select_no_filter)->toArray();

        if (count($actions_array) > 0)
        {
            $last_action = end($actions_array);
            $id = $last_action['action_id'] - 1;
        }
        if (count($actions_array) < $limit || count($actions_array) <= 0)
        {
            $end_of_feed = true;
        }

        // Privacy check
        $uid = (isset($user->user_id) ? $user->user_id : 0);
        $wall_tb = 'engine4_wall_privacies';
        $tagTable = Engine_Api::_()->getDbtable('tags', 'wall');
        $tagName = $tagTable->info('name');
        $tagselect = $tagTable->select()->from($tagName);
        if ($about->getType() == 'group'){
            $tagselect->where('taggeduser_id = ?', 0); //Quick fix for tagging a friend who has userID equals a groupID
        } else {    
            if ($uid != $about->getIdentity())
            {
                //$tagselect->where('user_id = ?', $uid);
                $tagselect->where('taggeduser_id = ?', $about->getIdentity());
            }
            else
            {
                $tagselect->where('user_id <> ?', $uid);
                $tagselect->where('taggeduser_id = ?', $uid);
            }
        } 
        
        if ($tagTable->fetchAll($tagselect)->toArray() != null)
        {
            $ftags = $tagTable->fetchAll($tagselect)->toArray();
            $ftag = array();
            foreach ($ftags as $ft)
            {
                $ftag[] = $ft['action_id'];
            }
            $ftag = array_merge($ids, $ftag);
            $ftag = array_unique($ftag);


            $wallselect = $this->select()
                    ->setIntegrityCheck(false)
                    ->from(array('a' => 'engine4_activity_actions'))
                    ->joinLeft(array('p' => "$wall_tb"), 'p.action_id = a.action_id', 'p.privacy')
                    ->where('a.action_id IN(' . join(',', $ftag) . ')')
                    ->where("(a.action_id NOT IN (SELECT action_id FROM $wall_tb)
	              OR p.user_id = $uid
	              OR (p.privacy = 2 AND p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid AND resource_approved = 1 AND user_approved = 1))
	              OR (p.privacy = 1 AND (p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid)
	                                     OR p.user_id IN (SELECT DISTINCTROW m2.resource_id
	                                                  FROM engine4_user_membership AS m1, engine4_user_membership AS m2
	                                                  WHERE m1.user_id = $uid
	                                                        AND m1.resource_id = m2.user_id
	                                                            AND (m1.resource_approved = 1 AND m1.user_approved = 1)
	                                                                AND (m2.resource_approved = 1 AND m2.user_approved = 1))
	                                     )
	                 )
                      OR (p.privacy > 2 AND $uid in (SELECT object_id FROM `engine4_wall_lists` as `list`
                                                        LEFT JOIN `engine4_wall_listitems` as `listitem`
                                                        ON list.list_id = listitem.list_id 
                                                        WHERE list.list_id = p.privacy 
                                                            AND object_type = 'friend' ))
	
	              )");
        }
        else
        {
            $wallselect = $this->select()
                    ->setIntegrityCheck(false)
                    ->from(array('a' => 'engine4_activity_actions'))
                    ->joinLeft(array('p' => "$wall_tb"), 'p.action_id = a.action_id', 'p.privacy')
                    ->where('a.action_id IN(' . join(',', $ids) . ')')
                    ->where("(a.action_id NOT IN (SELECT action_id FROM $wall_tb)
	              OR p.user_id = $uid
	              OR (p.privacy = 2 AND p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid AND resource_approved = 1 AND user_approved = 1))
	              OR (p.privacy = 1 AND (p.user_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid)
	                                     OR p.user_id IN (SELECT DISTINCTROW m2.resource_id
	                                                  FROM engine4_user_membership AS m1, engine4_user_membership AS m2
	                                                  WHERE m1.user_id = $uid
	                                                        AND m1.resource_id = m2.user_id
	                                                            AND (m1.resource_approved = 1 AND m1.user_approved = 1)
	                                                                AND (m2.resource_approved = 1 AND m2.user_approved = 1))
	                                     )
	                 )
                      OR (p.privacy > 2 AND $uid in (SELECT object_id FROM `engine4_wall_lists` as `list`
                                                        LEFT JOIN `engine4_wall_listitems` as `listitem`
                                                        ON list.list_id = listitem.list_id 
                                                        WHERE list.list_id = p.privacy 
                                                            AND object_type = 'friend' ))
	
	              )");
        }

        //Filter
        switch ($filter_type)
        {
            case 'list':
                if (null != $filter_id)
                {
                    $wallselect->where("a.subject_type = 'user' and a.subject_id IN (
                			SELECT object_id FROM `engine4_wall_lists` as `list`
                			LEFT JOIN `engine4_wall_listitems` as `listitem`
                			ON list.list_id = listitem.list_id
                			WHERE list.list_id = $filter_id AND object_type = 'friend' )
                			");
                }
                break;
            case 'friend':
                $wallselect->where("a.subject_type = 'user' and a.subject_id IN (SELECT resource_id FROM engine4_user_membership WHERE user_id = $uid AND resource_approved = 1 AND user_approved = 1)");
                break;
            case 'blog':
                $wallselect->where("a.object_type = 'blog'");
                break;
            case 'album':
                $wallselect->where("a.object_type = 'album' or a.object_type = 'advalbum_album'");
                break;
            case 'status':
                $wallselect->where("a.type = 'status'");
                break;
        }

//        Zend_Registry::get('Zend_Log')->log('-------------------Wall Select ACTIVITY ABOUT-------------------', 7);
//        Zend_Registry::get('Zend_Log')->log($wallselect, 7);
//        Zend_Registry::get('Zend_Log')->log('-----------------------------------------------------------', 7);
        
        $privacyAc = $this->fetchAll($wallselect)->toArray();
        $priAc = array();
        foreach ($privacyAc as $pri)
        {
            $priAc[] = $pri['action_id'];
        }
        if (count($priAc) < 1)
            return;

        $select = $this->select()
                ->setIntegrityCheck(false);
        $select->from(array('a' => 'engine4_activity_actions'))
                ->where('a.action_id IN(' . join(',', $priAc) . ')');
//        if ($ajax_call)
//            $limit = 1;
        $select->order('a.action_id DESC')
                ->limit($limit);
        
        return $this->fetchAll($select);
    }

    public function attachActivity($action, Core_Model_Item_Abstract $attachment, $mode = 1)
    {
        $attachmentTable = Engine_Api::_()->getDbtable('attachments', 'activity');

        if (is_numeric($action))
        {
            $action = $this->fetchRow($this->select()->where('action_id = ?', $action)->limit(1));
        }

        if (!($action instanceof Activity_Model_Action))
        {
            $eInfo = ( is_object($action) ? get_class($action) : $action );
            throw new Activity_Model_Exception(sprintf('Invalid action passed to attachActivity: %s', $eInfo));
        }

        $attachmentRow = $attachmentTable->createRow();
        $attachmentRow->action_id = $action->action_id;
        $attachmentRow->type = $attachment->getType();
        $attachmentRow->id = $attachment->getIdentity();
        $attachmentRow->mode = (int) $mode;
        $attachmentRow->save();

        $action->attachment_count++;
        $action->save();

        return $this;
    }

    // Actions

    public function getActionById($action_id)
    {
        return $this->find($action_id)->current();
    }

    public function getActionsByObject(Core_Model_Item_Abstract $object)
    {
        $select = $this->select()->where('object_type = ?', $object->getType())
                ->where('object_id = ?', $object->getIdentity());
        return $this->fetchAll($select);
    }

    public function getActionsBySubject(Core_Model_Item_Abstract $subject)
    {
        $select = $this->select()
                ->where('subject_type = ?', $subject->getType())
                ->where('subject_id = ?', $subject->getIdentity())
        ;

        return $this->fetchAll($select);
    }

    public function getActionsByAttachment(Core_Model_Item_Abstract $attachment)
    {
        // Get all action ids from attachments
        $attachmentTable = Engine_Api::_()->getDbtable('attachments', 'activity');
        $select = $attachmentTable->select()
                ->where('type = ?', $attachment->getType())
                ->where('id = ?', $attachment->getIdentity())
        ;

        $actions = array();
        foreach ($attachmentTable->fetchAll($select) as $attachmentRow)
        {
            $actions[] = $attachmentRow->action_id;
        }

        // Get all actions
        $select = $this->select()
                ->where('action_id IN(\'' . join("','", $ids) . '\')')
        ;

        return $this->fetchAll($select);
    }

    // Utility

    /**
     * Add an action-privacy binding
     *
     * @param int $action_id
     * @param string $type
     * @param Core_Model_Item_Abstract $subject
     * @param Core_Model_Item_Abstract $object
     * @return int The insert id
     */
    public function addActivityBindings($action)
    {
        // Get privacy bindings
        $event = Engine_Hooks_Dispatcher::getInstance()->callEvent('addActivity', array(
            'subject' => $action->getSubject(),
            'object' => $action->getObject(),
            'type' => $action->type,
                ));

        // Add privacy bindings
        $streamTable = Engine_Api::_()->getDbtable('stream', 'activity');
        foreach ((array) $event->getResponses() as $response)
        {
            if (isset($response['target']))
            {
                $target_type = $response['target'];
                $target_id = 0;
            }
            else if (isset($response['type']) && isset($response['identity']))
            {
                $target_type = $response['type'];
                $target_id = $response['identity'];
            }
            else
            {
                continue;
            }

            $streamTable->insert(array(
                'action_id' => $action->action_id,
                'type' => $action->type,
                'target_type' => (string) $target_type,
                'target_id' => (int) $target_id,
                'subject_type' => $action->subject_type,
                'subject_id' => $action->subject_id,
                'object_type' => $action->object_type,
                'object_id' => $action->object_id,
            ));
        }
        return $this;
    }

    public function clearActivityBindings($action)
    {
        $streamTable = Engine_Api::_()->getDbtable('stream', 'activity');
        $streamTable->delete(array(
            'action_id = ?' => $action->getIdentity(),
        ));
    }

    public function resetActivityBindings($action)
    {
        $this->clearActivityBindings($action);
        $this->addActivityBindings($action);
        return $this;
    }

    // Types

    /**
     * Gets action type meta info
     *
     * @param string $type
     * @return Engine_Db_Row
     */
    public function getActionType($type)
    {
        return $this->getActionTypes()->getRowMatching('type', $type);
    }

    /**
     * Gets all action type meta info
     *
     * @param string|null $type
     * @return Engine_Db_Rowset
     */
    public function getActionTypes()
    {
        if (null === $this->_actionTypes)
        {
            $table = Engine_Api::_()->getDbtable('actionTypes', 'activity');
            $this->_actionTypes = $table->fetchAll();
        }

        return $this->_actionTypes;
    }

    // Utility

    protected function _getInfo(array $params)
    {
        $settings = Engine_Api::_()->getApi('settings', 'core');
        $args = array(
            'limit' => $settings->getSetting('activity.length', 20),
            'action_id' => null,
            'max_id' => null,
            'min_id' => null,
            //'ajax_call' => false,
            'filter_type' => 'default',
            'filter_id' => null,
        );

        $newParams = array();
        foreach ($args as $arg => $default)
        {
            if (!empty($params[$arg]))
            {
                $newParams[$arg] = $params[$arg];
            }
            else
            {
                $newParams[$arg] = $default;
            }
        }

        return $newParams;
    }

}